using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEditor;

namespace UnityEngine.AssetBundles
{
    [System.Serializable]
    public class BuildBundleNameTab
    {
        private BuildBundleInfo mToolInfo;

        private Vector2 m_ScrollPosition;
        private AssetBundleBrowserMain mParent;

        private GUIStyle[] m_Style = new GUIStyle[2];
        Vector2 m_Dimensions = new Vector2(0, 0);

        string[] exceptFileExt = { ".cs", ".meta" };

        //---------------------------------------------------------------------------------

        public void OnEnable(Rect pos, AssetBundleBrowserMain parent)
        {
            mParent = parent;
            LoadUserData();
        }

        public void OnDisable()
        {
            SaveUserData();
        }

        public void OnGUI(Rect pos)
        {
            SkinInit();

            m_ScrollPosition = EditorGUILayout.BeginScrollView(m_ScrollPosition);

            GUILayout.Space(20);
            var lableSkin = GUI.skin.GetStyle("Label");
            Color oldcolor = lableSkin.normal.textColor;
            lableSkin.normal.textColor = Color.grey;
            lableSkin.alignment = TextAnchor.MiddleLeft;

            GUILayout.Label(
                "注意事项 :" +
                "\n--> 设定的路径不当有包含关系" +
                "\n--> All 整个文件夹及子文件夹是一个Bundle，名字为路径最后一个文件夹名" +
                "\n--> Single 整个文件夹及子文件夹中每个给定后缀的文件是一个Bundle" +
                "\n--> Auto 给定路径中有文件的文件夹是一个Bundle，名字为相对路径名" +
                "\n--> 文件过滤 不填 默认为所有" +
                "\n--> 文件过滤后缀以- ; - 分号隔开", lableSkin);

            lableSkin.normal.textColor = oldcolor;

            GUILayout.Space(20);
            EditorGUILayout.LabelField("---包处理根目录---");
            ShowResPath();

            GUILayout.Space(20);
            EditorGUILayout.BeginHorizontal();
            if (GUILayout.Button("刷新Bundle名称"))
            {
                DeleteBundleName();
                SetBundleName();
                mParent.m_InspectTab.RefreshBundles();
                mParent.ShowNotification(new GUIContent("Refresh BundleName Done!"));
            }
            if (GUILayout.Button("清除无用名称", GUILayout.Width(128)))
            {
                ClearUnusedName();
                mParent.m_InspectTab.RefreshBundles();
                mParent.ShowNotification(new GUIContent("Clear BundleName Done!"));
            }
            if (GUILayout.Button("删除全部名称", GUILayout.Width(128)))
            {
                DeleteBundleName();
                mParent.m_InspectTab.RefreshBundles();
                mParent.ShowNotification(new GUIContent("Delete BundleName Done!"));
            }
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.EndScrollView();
        }

        //----------------------------------------------------------------------------------------

        public void DeleteBundleName()
        {
            string[] bundleNames = AssetDatabase.GetAllAssetBundleNames();
            for (int i = 0; i < bundleNames.Length; i++)
            {
                AssetDatabase.RemoveAssetBundleName(bundleNames[i], true);
            }
        }

        public void ClearUnusedName()
        {
            AssetDatabase.RemoveUnusedAssetBundleNames();
        }

        public void SetBundleName()
        {
            foreach (var path in mToolInfo.Paths)
            {
                switch (path.PackType)
                {
                    case BuildBundleInfo.ePackType.All:
                        SetBundleName_All(Path.GetFileName(path.RootPath), path);
                        break;
                    case BuildBundleInfo.ePackType.Single:
                        SetBundleName_Single(path.RootPath, path);
                        break;
                    case BuildBundleInfo.ePackType.Auto:
                        SetBundleName_Auto(path.RootPath, path.Pattern, null);
                        break;
                    default: break;
                }

            }
        }

        public void SetBundleName_All(string BundleName, BuildBundleInfo.BundlePathInfo pathInfo)
        {
            DirectoryInfo directoryInfo = new DirectoryInfo(pathInfo.RootPath);
            if (!directoryInfo.Exists)
            {
                return;
            }
            FileInfo[] filePaths = null;
            DirectoryInfo[] dirPaths = null;
            GetDirsFiles(directoryInfo, pathInfo.Pattern, out filePaths, out dirPaths);

            if (filePaths.Length > 0)
            {
                SetBundleNameForGiven(BundleName, "", filePaths);
            }

            for (int i = 0; i < dirPaths.Length; i++)
            {
                SetBundleName_All(BundleName, pathInfo);
            }
        }

        public void SetBundleName_Single(string curPath, BuildBundleInfo.BundlePathInfo pathInfo)
        {
            DirectoryInfo directoryInfo = new DirectoryInfo(curPath);
            if (!directoryInfo.Exists)
            {
                return;
            }
            FileInfo[] filePaths = null;
            DirectoryInfo[] dirPaths = null;
            GetDirsFiles(directoryInfo, pathInfo.Pattern, out filePaths, out dirPaths);

            string baseName = Path.GetFileNameWithoutExtension(pathInfo.RootPath);

            for (int i = 0; i < filePaths.Length; i++)
            {
                string extraName = Path.GetFileNameWithoutExtension(filePaths[i].FullName);
                string BundleName = baseName + "/" + extraName;

                SetBundleNameForGiven(BundleName, "", filePaths[i]);
            }

            for (int i = 0; i < dirPaths.Length; i++)
            {
                SetBundleName_Single(dirPaths[i].FullName, pathInfo);
            }
        }

        public void SetBundleName_Auto(string curPath, string usePattern, string preBundleName)
        {
            DirectoryInfo directoryInfo = new DirectoryInfo(curPath);
            if (!directoryInfo.Exists)
            {
                return;
            }
            FileInfo[] filePaths = null;
            DirectoryInfo[] dirPaths = null;
            GetDirsFiles(directoryInfo, usePattern, out filePaths, out dirPaths);

            string BundleName = string.IsNullOrEmpty(preBundleName) ? Path.GetFileName(curPath) : preBundleName + "/" + Path.GetFileName(curPath);

            if (filePaths.Length > 0)
            {
                SetBundleNameForGiven(BundleName, "", filePaths);
            }
            else
            {
                for (int i = 0; i < dirPaths.Length; i++)
                {
                    SetBundleName_Auto(dirPaths[i].FullName, usePattern, BundleName);
                }
            }
        }

        public void ShowResPath()
        {
            if (mToolInfo.Paths == null || mToolInfo.Paths.Count == 0)
            {
                if (GUILayout.Button("添加新目录"))
                {
                    if (mToolInfo.Paths == null)
                    {
                        mToolInfo.Paths = new List<BuildBundleInfo.BundlePathInfo>();
                    }
                    mToolInfo.Paths.Add(new BuildBundleInfo.BundlePathInfo());
                }
            }

            for (int i = 0; i < mToolInfo.Paths.Count; i++)
            {
                BuildBundleInfo.BundlePathInfo path = mToolInfo.Paths[i];

                EditorGUILayout.BeginHorizontal(m_Style[i % 2]);

                EditorGUILayout.LabelField("描述: ", GUILayout.Width(32));
                path.Desc = EditorGUILayout.TextField(path.Desc, GUILayout.Width(128));

                EditorGUILayout.LabelField("分包:", GUILayout.Width(32));
                path.PackType = (BuildBundleInfo.ePackType)EditorGUILayout.EnumPopup(path.PackType, GUILayout.Width(64));

                EditorGUILayout.LabelField(new GUIContent("过滤: ", "留下给定后缀名的文件"), GUILayout.Width(32));
                path.Pattern = EditorGUILayout.TextField(path.Pattern, GUILayout.Width(64));

                path.RootPath = ShowFolderLabel("路径: ", path.RootPath, Application.dataPath);

                if (GUILayout.Button("添加", GUILayout.Width(48)))
                {
                    mToolInfo.Paths.Insert(i + 1, new BuildBundleInfo.BundlePathInfo());
                }

                if (GUILayout.Button("删除", GUILayout.Width(48)))
                {
                    mToolInfo.Paths.Remove(path);
                }
                EditorGUILayout.EndHorizontal();
            }
        }

        //----------------------------------------------------------------------------------------

        /// <summary>
        /// 为给定资源设置相同BundleName
        /// </summary>
        private void SetBundleNameForGiven(string bundleName, string Variant, params FileInfo[] filePaths)
        {
            bool isSenceBundle = false;
            for (int i = 0; i < filePaths.Length; i++)
            {
                if (filePaths[i].Extension.ToLower() == ".unity")
                {
                    isSenceBundle = true;
                    break;
                }
            }

            for (int i = 0; i < filePaths.Length; i++)
            {
                string orgPath = filePaths[i].FullName.Replace(@"\", "/");
                string assetPath = "Assets" + orgPath.Replace(Application.dataPath, "");

                if (isSenceBundle && filePaths[i].Extension != ".unity")
                {
                    Debug.LogError("场景Bundle 应当仅含有其相关的文件！" + assetPath);
                    continue;
                }

                AssetImporter import = AssetImporter.GetAtPath(assetPath);
                if (import == null)
                {
                    Debug.LogWarning("该路径支援不能被设置BundleName : " + assetPath);
                    continue;
                }

                import.SetAssetBundleNameAndVariant(bundleName, Variant);

                if (!isSenceBundle)
                {
                    SetBundleNameForDependence(assetPath, bundleName, Variant);
                }
            }
        }

        /// <summary>
        /// 为某个资源的依赖资源也设置同样的bundleName 
        /// </summary>
        /// <param name="assetPath"></param>
        /// <param name="bundleName"></param>
        private void SetBundleNameForDependence(string assetPath, string bundleName, string Variant = "")
        {
            string[] deps = AssetDatabase.GetDependencies(assetPath);
            for (int i = 0; i < deps.Length; i++)
            {
                AssetImporter import = AssetImporter.GetAtPath(deps[i]);
                if (import == null)
                {
                    Debug.LogWarning("该路径支援不能被设置BundleName : " + assetPath);
                    continue;
                }

                FileInfo fileInfo = new FileInfo(import.assetPath);
                if (CheckExceptUse(fileInfo.Extension, exceptFileExt))
                {
                    import.SetAssetBundleNameAndVariant(bundleName, Variant);
                }
            }
        }

        /// <summary>
        /// 过滤文件
        /// </summary>
        private bool CheckExceptUse(string param, string[] exceptPattern)
        {
            for (int i = 0; i < exceptPattern.Length; i++)
            {
                if (param.ToLower() == exceptPattern[i].ToLower())
                {
                    return false;
                }
            }
            return true;
        }

        private bool CheckInUse(string param, string pattern)
        {
            string[] patterns = pattern.Split(';', '；');
            if (pattern.Length <= 0) return true;

            for (int i = 0; i < patterns.Length; i++)
            {
                if (param.ToLower() == patterns[i].ToLower())
                {
                    return true;
                }
            }
            return false;
        }

        private void GetDirsFiles(DirectoryInfo directoryInfo, string usePattern, out FileInfo[] filePaths, out DirectoryInfo[] dirPaths)
        {
            filePaths = directoryInfo.GetFiles();
            dirPaths = directoryInfo.GetDirectories();

            List<FileInfo> filePathsList = filePaths.ToList();
            filePathsList = filePathsList.FindAll(o => CheckExceptUse(o.Extension, exceptFileExt));
            if (!string.IsNullOrEmpty(usePattern))
                filePathsList = filePathsList.FindAll(o => CheckInUse(o.Extension, usePattern));
            filePaths = filePathsList.ToArray();

            if (filePaths.Length > 0 && dirPaths.Length > 0 && usePattern != ".unity")
            {
                Debug.LogError("资源管理理当不应有资源和文件夹在一个层级的情况。" + directoryInfo.FullName);
            }
        }

        //----------------------------------------------------------------------------------------

        private string ShowFolderLabel(string title, string content, string defaultFolder)
        {
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField(title, GUILayout.Width(32));
            EditorGUILayout.LabelField(content);
            if (GUILayout.Button("修改路径", GUILayout.Width(96)))
            {
                string dicPath = EditorUtility.OpenFolderPanel("Select Path", content, defaultFolder);
                if (string.IsNullOrEmpty(dicPath))
                {
                    mParent.ShowNotification(new GUIContent("Sure ThePath is Right!"));
                    return content;
                }
                content = dicPath;
            }
            EditorGUILayout.EndHorizontal();
            return content;
        }

        private void DrawOutline(Rect rect, float size)
        {
            Color color = new Color(0.6f, 0.6f, 0.6f, 1.333f);
            if (EditorGUIUtility.isProSkin)
            {
                color.r = 0.12f;
                color.g = 0.12f;
                color.b = 0.12f;
            }

            if (Event.current.type != EventType.Repaint)
                return;

            Color orgColor = GUI.color;
            GUI.color = GUI.color * color;
            GUI.DrawTexture(new Rect(rect.x, rect.y, rect.width, size), EditorGUIUtility.whiteTexture);
            GUI.DrawTexture(new Rect(rect.x, rect.yMax - size, rect.width, size), EditorGUIUtility.whiteTexture);
            GUI.DrawTexture(new Rect(rect.x, rect.y + 1, size, rect.height - 2 * size), EditorGUIUtility.whiteTexture);
            GUI.DrawTexture(new Rect(rect.xMax - size, rect.y + 1, size, rect.height - 2 * size), EditorGUIUtility.whiteTexture);

            GUI.color = orgColor;
        }

        private void SkinInit()
        {
            m_Style[0] = "OL EntryBackOdd";
            m_Style[1] = "OL EntryBackEven";
            m_Style[0].wordWrap = true;
            m_Style[1].wordWrap = true;
            m_Style[0].padding = new RectOffset(4, 4, 0, 0);
            m_Style[1].padding = new RectOffset(4, 4, 0, 0);
        }

        private void LoadUserData()
        {
            string dataPath = Path.Combine(Application.dataPath, "Tools/Editor/BuildBundleInfo.dat");

            if (File.Exists(dataPath))
            {
                BinaryFormatter bf = new BinaryFormatter();
                FileStream file = null;
                try
                {
                    file = File.Open(dataPath, FileMode.Open);
                    if (file.Length > 0) mToolInfo = bf.Deserialize(file) as BuildBundleInfo;
                }
                catch
                {
                    if (mToolInfo == null) mToolInfo = new BuildBundleInfo();
                }

                if (file != null)
                {
                    file.Flush();
                    file.Close();
                    file.Dispose();
                }
            }
            else
            {
                mToolInfo = new BuildBundleInfo();
            }
        }

        private void SaveUserData()
        {
            string dataPath = Path.Combine(Application.dataPath, "Tools/Editor/BuildBundleInfo.dat");

            if (File.Exists(dataPath)) File.Delete(dataPath);

            BinaryFormatter bf = new BinaryFormatter();
            FileStream file = File.Create(dataPath);

            bf.Serialize(file, mToolInfo);
            file.Close();
        }

    }

    [System.Serializable]
    public class BuildBundleInfo
    {
        public enum ePackType
        {
            Auto = 0,
            All = 1,
            Single = 2,
        }

        [System.Serializable]
        public class BundlePathInfo
        {
            public string Pattern;
            public ePackType PackType = ePackType.Auto;
            public string Desc;

            private string mRootPath = "";
            public string RootPath
            {
                get
                {
                    return GetAbsoluteURL(mRootPath);
                }
                set
                {
                    mRootPath = SwitchAbsoluteURL(value);
                }
            }

            /// <summary>
            /// 将绝对路径转换到相对路径
            /// </summary>
            private string SwitchAbsoluteURL(string Absurl)
            {
                if (string.IsNullOrEmpty(Absurl))
                {
                    return "";
                }
                string relativeUrl = Application.dataPath;

                System.Uri uri1 = new Uri(Absurl);
                System.Uri uri2 = new Uri(relativeUrl);

                Uri relativeUri = uri2.MakeRelativeUri(uri1);

                return relativeUri.ToString();
            }

            /// <summary>
            /// 相对路径转到绝对路径
            /// </summary>
            private string GetAbsoluteURL(string relativeUrl)
            {
                if (string.IsNullOrEmpty(relativeUrl))
                {
                    return "";
                }
                string absURL = Path.GetFullPath(relativeUrl);
                return absURL;
            }

        }

        public List<BundlePathInfo> Paths = new List<BundlePathInfo>();

    }
}

